<?php
/**
 *
 * 搜索基类
 *
 * Created by IntelliJ IDEA.
 *
 * @author: hjh <hjh@jf.com>
 *
 * Date: 2017/10/26
 * Time: 15:55
 */

namespace Search;

use Common\Bridge;
use Common\DataType;
use lib\search\ElasticSearch;
use Queue\Queue;

class SearchBase
{
    // 圈子
    const CIRCLE_SEARCH = 'meite_circle/list/';
    // 帖子
    const TOPIC_SEARCH = 'meite_topic/list/';
    // 评论
    const TOPIC_COMMENT_SEARCH = 'meite_comment/list/';

    const FIELD_DATA_TYPE_INT = 'int';
    const FIELD_DATA_TYPE_STRING = 'string';
    const FIELD_DATA_TYPE_ARRAY = 'array';
    const FIELD_DATA_TYPE_DOUBLE = 'double';
    const FIELD_DATA_TYPE_DATE = 'date';

    // 字段Id
    const FIELD_ID = 'id';

    // 队列数据类型
    const QUEUE_DATA_ADD = 'add';
    const QUEUE_DATA_UPDATE = 'update';
    const QUEUE_DATA_DELETE = 'delete';

    const SORT_TYPE_ASC = 'asc';
    const SORT_TYPE_DESC = 'desc';

    // 子类要覆盖
    public static $SEARCH_FIELD_MAP = null;
    public static $QUEUE_NAME = null;
    public static $SEARCH_INDEX = null;

    /**
     * 添加数据
     *
     * @param $id
     * @param $info
     * @return bool
     */
    public static function add($id, $info)
    {
        assert(static::$QUEUE_NAME !== null);

        $data = static::formatAddData($id, $info);
        if ($data) {
            return self::addToQueue(static::$QUEUE_NAME, self::QUEUE_DATA_ADD, $data);
        }
        return false;
    }

    /**
     * 直接添加搜索数据到ES
     *
     * @param $id
     * @param $info
     * @return bool
     */
    public static function addDirect($id, $info)
    {
        $query = static::$SEARCH_INDEX. $id;

        $data = static::formatAddData($id, $info);
        if ($data) {
            $es = self::getES();
            $ret = $es->query($query, ElasticSearch::HTTP_PUT, $data);
            if ($ret === false) {
                debug_log(get_called_class() . ' addDirect failed: ' . json_encode($data),JF_LOG_ERROR);
            }
        }
        return false;
    }

    /**
     * 更新数据
     *
     * @param $id
     * @param $info
     * @return bool
     */
    public static function update($id, $info)
    {
        assert(!empty(static::$QUEUE_NAME));

        $data = static::formatUpdateData($id, $info);
        if ($data) {
            return self::addToQueue(static::$QUEUE_NAME, self::QUEUE_DATA_UPDATE, $data);
        }
        return false;
    }

    /**
     * 更新数据，直接操作搜索殷勤
     *
     * @param $id
     * @param $info
     * @return bool
     */
    public static function updateDirect($id, $info)
    {
        $query = static::$SEARCH_INDEX . $id . '/_update';

        $data = static::formatUpdateData($id, $info);
        if ($data) {
            $es = self::getES();
            $ret = $es->query($query, ElasticSearch::HTTP_POST, [
                'doc' => $data
            ]);
            if ($ret === false) {
                debug_log(get_called_class() . ' updateDirect failed: ' . json_encode($data),JF_LOG_ERROR);
            }
        }
        return false;
    }

    /**
     * 删除数据
     *
     * @param $id
     * @return bool
     */
    public static function delete($id)
    {
        assert(!empty(static::$QUEUE_NAME));

        return self::addToQueue(static::$QUEUE_NAME, self::QUEUE_DATA_DELETE, [self::FIELD_ID => $id]);
    }

    /**
     * 发送到队列
     *
     * @param string $name 队列名称
     * @param string $type 搜索类型
     * @param array $data 文档数据
     * @return bool
     */
    protected static function addToQueue($name, $type, $data)
    {
        $ret = Queue::add($name, [
            'type' => $type,
            'data' => $data
        ]);

        $logLevel = JF_LOG_DEBUG;
        if (empty($ret)) {
            $logLevel = JF_LOG_ERROR;
        }
        debug_log("addToQueue: $name $type failed", $logLevel);
        return $ret;
    }

    /**
     * 格式化 添加搜索的数据
     *
     * @param $id
     * @param $info
     * @return array|bool
     */
    public static function formatAddData($id, $info)
    {
        assert(!empty(static::$SEARCH_FIELD_MAP));

        $diffKey = array_diff_key(static::$SEARCH_FIELD_MAP, $info);
        if (count($diffKey) > 0) {
            debug_log(['formatAddData ', $diffKey], JF_LOG_ERROR);
            return false;
        }
        $data = [self::FIELD_ID => $id];
        foreach (static::$SEARCH_FIELD_MAP as $key => $value) {
            $data[$value[0]] = self::formatData($value[1], $info[$key]);
        }
        return $data;
    }

    /**
     * 格式化更新数据
     *
     * @param $id
     * @param $info
     * @return array|bool
     */
    public static function formatUpdateData($id, $info)
    {
        assert(!empty(static::$SEARCH_FIELD_MAP));

        $data = [];
        foreach (static::$SEARCH_FIELD_MAP as $key => $value) {
            if (!isset($info[$key])) {
                continue;
            }
            $data[$value[0]] = self::formatData($value[1], $info[$key]);
        }
        if (count($data) == 0) {
             debug_log('formatUpdateData failed, data is empty', JF_LOG_ERROR);
            return false;
        }
        $data[self::FIELD_ID] = $id;
        return $data;
    }

    /**
     * 根据数据类型格式化数据
     *
     * @param $dataType
     * @param $value
     * @return array|int
     */
    protected static function formatData($dataType, $value)
    {
        switch ($dataType) {
            case self::FIELD_DATA_TYPE_INT:
                return (int)$value;
                break;
            case self::FIELD_DATA_TYPE_ARRAY:
                if (empty($value)) {
                    return [];
                }
                if (!is_array($value)) {
                    $value = json_decode($value, JSON_UNESCAPED_UNICODE);
                }
                return self::buildArray($value);
                break;
            case self::FIELD_DATA_TYPE_DATE:
                if (strpos($value, '0000-00-00') === false && !empty($value)) {
                    return $value;
                } else {
                    return DataType::datetime(0);
                }
            default:
                return $value;
                break;
        }
    }

    /**
     * 格式化数组
     *
     * @param $arr
     * @return array
     */
    protected static function buildArray($arr)
    {
        $data = [];
        foreach ($arr as $k => $v) {
            $data[] = $v;
        }
        return $data;
    }

    protected static function getSearchResults($query, $fields)
    {
        $ret = self::query($query);

        $total = empty($ret['hits']['total']) ? 0 : $ret['hits']['total'];
        $data = [];
        if ($ret != false) {
            $hits = $ret['hits']['hits'];
            foreach ($hits as $result) {
                $items = [];
                foreach ($fields as $key) {
                    $items[$key] = $result['_source'][$key];
                }
                $data[] = $items;
            }
        }
        return ['total' => $total, 'data' => $data];
    }

    protected static function query($query_str)
    {
        assert(!empty(static::$SEARCH_INDEX));

        $es = self::getES();

        return $es->query(static::$SEARCH_INDEX . '_search', ElasticSearch::HTTP_POST, $query_str);
    }

    protected static function getES()
    {
        return Bridge::search(Bridge::SEARCH_DEFAULT);
    }
}
